package org.swxjava.assembler.avm1;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.swxjava.assembler.Constants;
import org.swxjava.assembler.ObjectCompiler;
import org.swxjava.util.Log;
import org.swxjava.util.LogFactory;

public class AsArray implements ObjectCompiler {

	/** Logger */
	private static final Log LOG = LogFactory.getLog(AsArray.class);

	/** Array internal map */
	private List<AsArrayElement> elements;
	private AsArrayElement lastElement = null;
	
	public AsArray() {
		this.elements = new ArrayList<AsArrayElement>();
	}
	
	public int getByteSize() {
		if(elements == null || elements.size() == 0) {
			return 5; // INT int(4) = ArrayLength
		} else {
			return elements.get(0).getByteSize();
		}
	}

	public boolean add(AsArrayElement element) {
		if(lastElement != null) {
			lastElement.setNext(element);
		}
		lastElement = element;
		return elements.add(element);
	}
	
	public byte[] toBytecode() {
		// TODO Auto-generated method stub
		return null;
	}

	public String toString() {
		StringBuffer bytecode = new StringBuffer();
		
		// First push
		if(elements.isEmpty() || !elements.get(0).isComplex()) {
			bytecode.append(Constants.ACTION_PUSH);
			ObjectCompiler firstByteLength = new AsByteLength(getByteSize());
			bytecode.append(firstByteLength.toString());
			if(LOG.isDebugEnabled()) {
				LOG.debug("Array : " + Constants.ACTION_PUSH + " " + firstByteLength.toString());
			}
		}
		
		// Processing for each elements
		AsArrayElement lastElement = null;
		for (Iterator<AsArrayElement> it = elements.iterator(); it.hasNext();) {
			AsArrayElement element = it.next();

			// TODO Check if the current bytecode length is approaching
			// the 64K (65535 byte) limit that we can store in a single push
			// and, if so, create a new push.

			// Need to PUSH
			if (lastElement != null 
					&& (lastElement.isComplex() || lastElement.isNew()) 
					&& (!element.isComplex() && !element.isNew())) {
				LOG.debug("(at end) IS COMPLEX - " + lastElement.value().getClass().getName());
				// PUSH BYTESIZE
				bytecode.append(Constants.ACTION_PUSH);
				ObjectCompiler size = new AsByteLength(element.getByteSize());
				bytecode.append(size.toString());
				if(LOG.isDebugEnabled()) {
					LOG.debug(" - push : " + Constants.ACTION_PUSH + " " + size.toString());
				}
			}
			// VALUE VALUE ....
			String value = element.value().toString();
			if(LOG.isDebugEnabled()) {
				LOG.debug(" - value : " + value);
			}
			bytecode.append(value);
			lastElement = element;
		}

		// Store number of properties.
		ObjectCompiler arrayLength = new AsInt(elements.size());

		// Need to PUSH
		if (lastElement != null && (lastElement.isComplex() || lastElement.isNew())) {
			LOG.debug("(at end) IS COMPLEX - " + lastElement.value().getClass().getName());
			// PUSH BYTESIZE
			bytecode.append(Constants.ACTION_PUSH);
			ObjectCompiler propLengthBytesize = new AsByteLength(arrayLength.getByteSize());
			bytecode.append(propLengthBytesize.toString());
			if(LOG.isDebugEnabled()) {
				LOG.debug(" - push : " + Constants.ACTION_PUSH + " " + propLengthBytesize.toString());
			}
		}

		bytecode.append(arrayLength.toString());
		if(LOG.isDebugEnabled()) {
			LOG.debug(" - length : " + arrayLength.toString());
		}

		// ACTION_INIT_ARRAY
		bytecode.append(Constants.ACTION_INIT_ARRAY);
		if(LOG.isDebugEnabled()) {
			LOG.debug(" - initArray : " + Constants.ACTION_INIT_ARRAY);
		}

		return bytecode.toString();
	}
}
